﻿using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace PSE.Deployment.SetupBase.UI.CustomComponents
{
	public static class VirtualToggleButton
	{
		public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(Nullable<bool>), typeof(VirtualToggleButton), new FrameworkPropertyMetadata((Nullable<bool>)false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnIsCheckedChanged)));
		public static readonly DependencyProperty IsThreeStateProperty = DependencyProperty.RegisterAttached("IsThreeState", typeof(bool), typeof(VirtualToggleButton), new FrameworkPropertyMetadata((bool)false));
		public static readonly DependencyProperty IsVirtualToggleButtonProperty = DependencyProperty.RegisterAttached("IsVirtualToggleButton", typeof(bool), typeof(VirtualToggleButton), new FrameworkPropertyMetadata((bool)false, new PropertyChangedCallback(OnIsVirtualToggleButtonChanged)));

		public static Nullable<bool> GetIsChecked(DependencyObject d)
		{
			return (Nullable<bool>)d.GetValue(IsCheckedProperty);
		}
		public static bool GetIsThreeState(DependencyObject d)
		{
			return (bool)d.GetValue(IsThreeStateProperty);
		}
		public static bool GetIsVirtualToggleButton(DependencyObject d)
		{
			return (bool)d.GetValue(IsVirtualToggleButtonProperty);
		}
		public static void SetIsChecked(DependencyObject d, Nullable<bool> value)
		{
			d.SetValue(IsCheckedProperty, value);
		}
		public static void SetIsThreeState(DependencyObject d, bool value)
		{
			d.SetValue(IsThreeStateProperty, value);
		}
		public static void SetIsVirtualToggleButton(DependencyObject d, bool value)
		{
			d.SetValue(IsVirtualToggleButtonProperty, value);
		}

		internal static RoutedEventArgs RaiseCheckedEvent(UIElement target)
		{
			if (target == null) return null;

			RoutedEventArgs args = new RoutedEventArgs();
			args.RoutedEvent = ToggleButton.CheckedEvent;
			RaiseEvent(target, args);
			return args;
		}
		internal static RoutedEventArgs RaiseUncheckedEvent(UIElement target)
		{
			if (target == null) return null;

			RoutedEventArgs args = new RoutedEventArgs();
			args.RoutedEvent = ToggleButton.UncheckedEvent;
			RaiseEvent(target, args);
			return args;
		}
		internal static RoutedEventArgs RaiseIndeterminateEvent(UIElement target)
		{
			if (target == null) return null;

			RoutedEventArgs args = new RoutedEventArgs();
			args.RoutedEvent = ToggleButton.IndeterminateEvent;
			RaiseEvent(target, args);
			return args;
		}

		static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			UIElement pseudobutton = d as UIElement;
			if (pseudobutton != null)
			{
				Nullable<bool> newValue = (Nullable<bool>)e.NewValue;
				if (newValue == true)
				{
					RaiseCheckedEvent(pseudobutton);
				}
				else if (newValue == false)
				{
					RaiseUncheckedEvent(pseudobutton);
				}
				else
				{
					RaiseIndeterminateEvent(pseudobutton);
				}
			}
		}
		static void OnIsVirtualToggleButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			IInputElement element = d as IInputElement;
			if (element != null)
			{
				if ((bool)e.NewValue)
				{
					element.MouseLeftButtonDown += OnMouseLeftButtonDown;
					element.KeyDown += OnKeyDown;
				}
				else
				{
					element.MouseLeftButtonDown -= OnMouseLeftButtonDown;
					element.KeyDown -= OnKeyDown;
				}
			}
		}
		static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			e.Handled = true;
			UpdateIsChecked(sender as DependencyObject);
		}
		static void OnKeyDown(object sender, KeyEventArgs e)
		{
			if (e.OriginalSource == sender)
			{
				if (e.Key == Key.Space)
				{
					// ignore alt+space which invokes the system menu
					if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) return;

					UpdateIsChecked(sender as DependencyObject);
					e.Handled = true;

				}
				else if (e.Key == Key.Enter && (bool)(sender as DependencyObject).GetValue(KeyboardNavigation.AcceptsReturnProperty))
				{
					UpdateIsChecked(sender as DependencyObject);
					e.Handled = true;
				}
			}
		}
		static void UpdateIsChecked(DependencyObject d)
		{
			Nullable<bool> isChecked = GetIsChecked(d);
			if (isChecked == true)
			{
				SetIsChecked(d, GetIsThreeState(d) ? (Nullable<bool>)null : (Nullable<bool>)false);
			}
			else
			{
				SetIsChecked(d, isChecked.HasValue);
			}
		}
		static void RaiseEvent(DependencyObject target, RoutedEventArgs args)
		{
			if (target is UIElement)
			{
				(target as UIElement).RaiseEvent(args);
			}
			else if (target is ContentElement)
			{
				(target as ContentElement).RaiseEvent(args);
			}
		}
	}
}